package com.huxi.lang.file.xls;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.ZoneOffset;
import java.util.Objects;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.huxi.lang.local.DateUtils;

/**
 * 工作表格简单封装
 */
public class TableWorkbook {

	private Workbook workbook;
	private ZoneOffset offset;

	public TableWorkbook(Workbook workbook, ZoneOffset offset) {
		this.workbook = Objects.requireNonNull(workbook);
		this.offset = offset != null ? offset : DateUtils.defaultZoneOffset;
	}

	public TableWorkbook(Workbook workbook) {
		this(workbook, DateUtils.defaultZoneOffset);
	}

	public Workbook getWorkbook() {
		return workbook;
	}

	/**
	 * 创建空工作表格
	 * 
	 * @param xlsx 是否XLSX格式
	 * @return 空工作表格
	 */
	public static TableWorkbook ofEmpty(boolean xlsx) {
		return ofEmpty(xlsx);
	}

	/**
	 * 创建空工作表格
	 * 
	 * @param xlsx   是否XLSX格式
	 * @param offset 日期时区
	 * @return 空工作表格
	 */
	public static TableWorkbook ofEmpty(boolean xlsx, ZoneOffset offset) {
		if (xlsx) {
			return new TableWorkbook(new XSSFWorkbook(), offset);
		} else {
			return new TableWorkbook(new HSSFWorkbook(), offset);
		}
	}

	/**
	 * 创建空工作表格
	 * 
	 * @param xlsx   是否XLSX格式
	 * @param stream 工作表格内容输入流，非空值。
	 * @param offset 日期时区
	 * @return 工作表格
	 * @throws IOException          表格内容加载失败
	 * @throws RuntimeException     工作表格内容解析失败
	 * @throws NullPointerException 输入流为空值
	 */
	public static TableWorkbook ofStream(boolean xlsx, InputStream stream, ZoneOffset offset) throws IOException {
		Objects.requireNonNull(stream);
		if (xlsx) {
			return new TableWorkbook(new XSSFWorkbook(stream), offset);
		} else {
			return new TableWorkbook(new HSSFWorkbook(stream), offset);
		}
	}

	public static TableWorkbook ofStream(boolean xlsx, InputStream stream) throws IOException {
		return ofStream(xlsx, stream, null);
	}

	/**
	 * 保存工作表格到输出流且关闭输出流
	 * 
	 * @param stream 输出流，非空值。
	 * @throws IOException          内容写入输出流失败
	 * @throws NullPointerException 输出流为空值
	 */
	public void saveAndClose(OutputStream stream) throws IOException {
		Objects.requireNonNull(stream, "工作表格内容保存输出流不能为空值");
		try {
			workbook.write(stream);
			OutputStream os = stream;
			stream = null;
			os.close();
		} catch (IOException en) {
			// TODO 日志记录
			throw en;
		} finally {
			if (stream != null) {
				try {
					stream.close();
				} catch (Throwable en) {
					// TODO 日志记录
				}
			}
			try {
				workbook.close();
			} catch (Throwable en) {
				// TODO 日志记录
			}
		}
	}

	/**
	 * 获取工作表格页总数
	 * 
	 * @return 表格页总数
	 */
	public int size() {
		return workbook.getNumberOfSheets();
	}

	/**
	 * 创建新的表格页
	 * 
	 * @param name 页名称，非空。
	 * @return 表格页，非空值。
	 * @throws IllegalArgumentException 页名称为空或重复
	 */
	public TableSheet createTableSheet(String name) throws IllegalArgumentException {
		if (name == null || name.length() == 0) {
			throw new IllegalArgumentException("创建表格时名称不能为空");
		}
		if (name.length() >= 30) {
			throw new IllegalArgumentException("创建表格时名称必须小于30个字符: " + name);
		}
		if (workbook.getSheet(name) != null) {
			throw new IllegalArgumentException("创建表格时名称不能重复: " + name);
		}
		return createTableSheet(workbook.createSheet(name));
	}

	private TableSheet createTableSheet(Sheet sheet) {
		Objects.requireNonNull(sheet, "表格页不能为空");
		return new TableSheet(sheet, offset);
	}

	/**
	 * 获取表格页
	 * 
	 * @param name 页名称
	 * @return 存在返回表格页，否则返回空值。
	 */
	public TableSheet getTableSheet(String name) {
		Sheet sheet = name != null ? workbook.getSheet(name) : null;
		return sheet != null ? createTableSheet(sheet) : null;
	}

	/**
	 * 获取表格页
	 * 
	 * @param index 页索引，大于0且小于表格页总数。
	 * @return 存在返回表格页，否则返回空值。
	 */
	public TableSheet getTableSheet(int index) {
		int len = size();
		if (index < 0) {
			index += len;
			if (index < 0) {
				return null;
			}
		} else if (index >= len) {
			return null;
		}
		return createTableSheet(workbook.getSheetAt(index));
	}

}
